package client;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.LineUnavailableException;

/**
 * A class used to generate sound
 * @author Marcin Bujar
 */
public class Oscillator {
	
	/*
	 * chromatic scale built on the note C, octave 4
	 * other octaves simply divide or multiply these
	 * (see playNote for details)
	 * 
	 * Note		Frequency [Hz]
	 * C		262
	 * C#		277
	 * D		294
	 * D#		311
	 * E		330
	 * F		349
	 * F#		370
	 * G		392
	 * G#		415
	 * A		440
	 * A#		466
	 * B		494
	*/


	/**
	 * Play sample using the soundcard
	 * @param frequency sample frequency in Hz
	 * @param length sound length in milliseconds
	 * @param volume sound volume (0.0 to 1.0)
	 */
	public static void playSound(double frequency, int length, double volume) throws IllegalArgumentException, LineUnavailableException {
		if(volume < 0.0 || volume > 1.0)
			throw new IllegalArgumentException("Volume " + volume + " out of range 0.0 to 1.0");
		if(length < 0 || length > 1000000)
			throw new IllegalArgumentException("Length " + length + " out of range 0 to 1000000");
		
		byte[] buffer = new byte[length * 8];
		
		/* create a sine wave */
		for(int i=0; i<buffer.length; i++){
			double angle = i / (8000.0 / frequency) * 2.0 * Math.PI;
			buffer[i] = (byte)(Math.sin(angle) * 127.0 * volume);
		}
		
		/* shape the waveform envelope */
		for(int i=0; i<20 && i<buffer.length/2; i++){
			buffer[i] = (byte)(buffer[i] * i / 20);
			buffer[buffer.length-1-i] = (byte)(buffer[buffer.length-1-i] * i / 20);
		}
		
		AudioFormat format = new AudioFormat(8000f, 8, 1, true, false);
		SourceDataLine line = AudioSystem.getSourceDataLine(format);
		
		line.open(format);
		line.start();
		line.write(buffer, 0, buffer.length);
		line.drain();
		line.close();
	}
	
	
	/**
	 * Play a note using the soundcard
	 * @param note note (A to G#), denoted A to GS
	 * @param octave octave number (-1 to 9)
	 * @param length note length in milliseconds
	 * @param volume note volume (0.0 to 1.0)
	 */
	public static void playNote(int note, int octave, int length, double volume)  throws IllegalArgumentException, LineUnavailableException {
		if(octave < -1 || octave > 9)
			throw new IllegalArgumentException("Octave " + octave + " out of range -1 to 9");
		
		double mul;
		
		switch(octave){
			case -1: mul = 1.0/32; break;
			case 0: mul = 1.0/16; break;
			case 1: mul = 1.0/8; break;
			case 2: mul = 1.0/4; break;
			case 3: mul = 1.0/2; break;
			case 4: mul = 1.0; break;
			case 5: mul = 2.0; break;
			case 6: mul = 4.0; break;
			case 7: mul = 8.0; break;
			case 8: mul = 16.0; break;
			case 9: mul = 32.0; break;
			default:
				mul = 1.0; break;
		}
		playSound(note*mul, length, volume);
	}

}
